﻿using Grand.Business.Core.Extensions;
using Grand.Business.Core.Interfaces.Catalog.Products;
using Grand.Business.Core.Interfaces.Checkout.Shipping;
using Grand.Business.Core.Interfaces.Common.Directory;
using Grand.Domain.Catalog;
using Grand.Domain.Shipping;
using Grand.Web.Features.Models.Common;
using Grand.Web.Features.Models.Orders;
using Grand.Web.Models.Orders;
using MediatR;

namespace Grand.Web.Features.Handlers.Orders;

public class GetShipmentDetailsHandler : IRequestHandler<GetShipmentDetails, ShipmentDetailsModel>
{
    private readonly CatalogSettings _catalogSettings;
    private readonly IDateTimeService _dateTimeService;
    private readonly IMediator _mediator;
    private readonly IProductService _productService;
    private readonly IShipmentService _shipmentService;
    private readonly ShippingProviderSettings _shippingProviderSettings;
    private readonly IShippingService _shippingService;

    private readonly ShippingSettings _shippingSettings;

    public GetShipmentDetailsHandler(
        IDateTimeService dateTimeService,
        IProductService productService,
        IShippingService shippingService,
        IShipmentService shipmentService,
        IMediator mediator,
        ShippingSettings shippingSettings,
        CatalogSettings catalogSettings,
        ShippingProviderSettings shippingProviderSettings)
    {
        _dateTimeService = dateTimeService;
        _productService = productService;
        _shippingService = shippingService;
        _shipmentService = shipmentService;
        _mediator = mediator;
        _shippingSettings = shippingSettings;
        _catalogSettings = catalogSettings;
        _shippingProviderSettings = shippingProviderSettings;
    }

    public async Task<ShipmentDetailsModel> Handle(GetShipmentDetails request, CancellationToken cancellationToken)
    {
        if (request.Shipment == null)
            throw new ArgumentNullException(nameof(request.Shipment));

        var model = new ShipmentDetailsModel {
            Id = request.Shipment.Id,
            ShipmentNumber = request.Shipment.ShipmentNumber
        };

        if (request.Shipment.ShippedDateUtc.HasValue)
            model.ShippedDate =
                _dateTimeService.ConvertToUserTime(request.Shipment.ShippedDateUtc.Value, DateTimeKind.Utc);
        if (request.Shipment.DeliveryDateUtc.HasValue)
            model.DeliveryDate =
                _dateTimeService.ConvertToUserTime(request.Shipment.DeliveryDateUtc.Value, DateTimeKind.Utc);

        //tracking number and shipment information
        if (!string.IsNullOrEmpty(request.Shipment.TrackingNumber))
        {
            model.TrackingNumber = request.Shipment.TrackingNumber;
            var srcm = _shippingService.LoadShippingRateCalculationProviderBySystemName(request.Order
                .ShippingRateProviderSystemName);
            if (srcm != null &&
                srcm.IsShippingRateMethodActive(_shippingProviderSettings))
            {
                var shipmentTracker = srcm.ShipmentTracker;
                if (shipmentTracker != null)
                {
                    model.TrackingNumberUrl = await shipmentTracker.GetUrl(request.Shipment.TrackingNumber);
                    if (_shippingSettings.DisplayShipmentEventsToCustomers)
                    {
                        var shipmentEvents = await shipmentTracker.GetShipmentEvents(request.Shipment.TrackingNumber);
                        if (shipmentEvents != null)
                            foreach (var shipmentEvent in shipmentEvents)
                            {
                                var shipmentStatusEventModel = new ShipmentDetailsModel.ShipmentStatusEventModel {
                                    Date = shipmentEvent.Date,
                                    EventName = shipmentEvent.EventName,
                                    Location = shipmentEvent.Location
                                };
                                model.ShipmentStatusEvents.Add(shipmentStatusEventModel);
                            }
                    }
                }
            }
        }

        //products in this shipment
        model.ShowSku = _catalogSettings.ShowSkuOnProductDetailsPage;
        foreach (var shipmentItem in request.Shipment.ShipmentItems)
        {
            var orderItem = request.Order.OrderItems.FirstOrDefault(x => x.Id == shipmentItem.OrderItemId);
            if (orderItem == null)
                continue;
            var product = await _productService.GetProductByIdIncludeArch(orderItem.ProductId);
            var shipmentItemModel = new ShipmentDetailsModel.ShipmentItemModel {
                Id = shipmentItem.Id,
                Sku = product.FormatSku(orderItem.Attributes),
                ProductId = orderItem.ProductId,
                ProductName = product.GetTranslation(x => x.Name, request.Language.Id),
                ProductSeName = product.GetSeName(request.Language.Id),
                AttributeInfo = orderItem.AttributeDescription,
                QuantityOrdered = orderItem.Quantity,
                QuantityShipped = shipmentItem.Quantity
            };

            model.Items.Add(shipmentItemModel);
        }

        //shipment notes 
        model.ShipmentNotes = await PrepareShipmentNotesModel(request);
        //order details model
        model.Order = await PrepareOrderModel(request);

        return model;
    }

    private async Task<IList<ShipmentDetailsModel.ShipmentNote>> PrepareShipmentNotesModel(GetShipmentDetails request)
    {
        var notes = new List<ShipmentDetailsModel.ShipmentNote>();
        foreach (var shipmentNote in (await _shipmentService.GetShipmentNotes(request.Shipment.Id))
                 .Where(on => on.DisplayToCustomer)
                 .OrderByDescending(on => on.CreatedOnUtc)
                 .ToList())
            notes.Add(new ShipmentDetailsModel.ShipmentNote {
                Id = shipmentNote.Id,
                ShipmentId = shipmentNote.ShipmentId,
                HasDownload = !string.IsNullOrEmpty(shipmentNote.DownloadId),
                Note = shipmentNote.Note,
                CreatedOn = _dateTimeService.ConvertToUserTime(shipmentNote.CreatedOnUtc, DateTimeKind.Utc)
            });
        return notes;
    }

    private async Task<ShipmentDetailsModel.OrderModel> PrepareOrderModel(GetShipmentDetails request)
    {
        var model = new ShipmentDetailsModel.OrderModel();
        var order = request.Order;
        model.UserFields = order.UserFields;
        model.Id = order.Id;
        model.OrderNumber = order.OrderNumber;
        model.OrderCode = order.Code;
        model.ShippingMethod = order.ShippingMethod;
        model.PickUpInStore = order.PickUpInStore;
        if (!order.PickUpInStore)
        {
            model.ShippingAddress = await _mediator.Send(new GetAddressModel {
                Language = request.Language,
                Address = order.ShippingAddress,
                ExcludeProperties = false
            });
        }
        else
        {
            if (order.PickupPoint is { Address: not null })
                model.PickupAddress = await _mediator.Send(new GetAddressModel {
                    Language = request.Language,
                    Address = order.PickupPoint.Address,
                    ExcludeProperties = false
                });
        }

        return model;
    }
}